package ods_ftp

import (
	"fmt"
	"github.com/jlaffaye/ftp"
	"io"
	"os"
	"server/global"
	"server/utils"
	"strconv"
	"strings"
	"time"
)

type OdsFtp struct {
	global.BaseModel
	FtpIp               string `json:"ftpIp" form:"ftpIp" gorm:"column:ftpIp;comment:ftpIp;size:100;"`
	FtpPort             string `json:"ftpPort" form:"ftpPort" gorm:"column:ftpPort;comment:ftp端口;size:20;"`
	FtpUser             string `json:"ftpUser" form:"ftpUser" gorm:"column:ftpUser;comment:ftp用户名;size:50;"`
	FtpPwd              string `json:"ftpPwd" form:"ftpPwd" gorm:"column:ftp_pwd;comment:ftp密码;size:100;"`
	FtpTimeOut          string `json:"ftpTimeOut" form:"ftpTimeOut" gorm:"column:ftpTimeOut;comment:登录超时;size:100;"`
	FtpPath             string `json:"ftpPath" form:"ftpPath" gorm:"column:ftpPath;comment:限定文件路径;size:255;"`
	FtpFolder           string `json:"ftpFolder" form:"ftpFolder" gorm:"column:ftpFolder;comment:限定文件夹;size:255;"`
	FtpFileName         string `json:"ftpFileName" form:"ftpFileName" gorm:"column:ftpFileName;comment:限定文件名;size:100;"`
	FtpFileType         string `json:"ftpFileType" form:"ftpFileType" gorm:"column:ftpFileType;comment:限定文件后缀;size:100;"`
	FtpFileLimitTime    string `json:"ftpFileLimitTime" form:"ftpFileLimitTime" gorm:"column:ftpFileLimitTime;comment:限定时间;size:100;"`
	FtpDelAfterDownload bool   `json:"ftpDelAfterDownload" form:"ftpDelAfterDownload" gorm:"column:ftpDelAfterDownload;comment:下载后删除;size:100;"`
	StoreServer   string `json:"storeServer" form:"storeServer" gorm:"column:storeServer;comment:存储服务器;size:255;"`
	StoreIp       string `json:"storeIp" form:"storeIp" gorm:"column:storeIp;comment:存储IP;size:255;"`
	StorePort     string `json:"storePort" form:"storePort" gorm:"column:storePort;comment:存储端口;size:255;"`
	StoreUser     string `json:"storeUser" form:"storeUser" gorm:"column:storeUser;comment:存储用户;size:255;"`
	StorePwd      string `json:"storePwd" form:"storePwd" gorm:"column:storePwd;comment:存储密码;size:255;"`
	StorePath     string `json:"storePath" form:"storePath" gorm:"column:storePath;comment:文件存储路径;size:255;"`
	StoreFolder   string `json:"storeFolder" form:"storeFolder" gorm:"column:storeFolder;comment:存储文件夹;size:100;"`
	StoreFileName string `json:"storeFileName" form:"storeFileName" gorm:"column:storeFileName;comment:存储文件名;size:255;"`
	StoreFileType string `json:"storeFileType" form:"storeFileType" gorm:"column:storeFileType;comment:存储文件后缀;size:255;"`
	JsonDb       string `json:"jsonDb" form:"jsonDb" gorm:"column:jsonDb;comment:解析json入库;"`
	JsonSeparate string `json:"jsonSeparate" form:"jsonSeparate" gorm:"column:jsonSeparate;comment:json内容分割符;size:50;"`
	JsonKey      string `json:"jsonKey" form:"jsonKey" gorm:"column:jsonKey;comment:json关键词;size:100;"`
	JsonMap      string `json:"jsonMap" form:"jsonMap" gorm:"column:jsonMap;comment:字段映射;size:100;"`

	OdsDbName string `json:"odsDbName" form:"odsDbName" gorm:"column:odsDbName;comment:ods库名;size:100;"`
	OdsTbName string `json:"odsTbName" form:"odsTbName" gorm:"column:odsTbName;comment:ods表名;size:100;"`
	CharSet   string `json:"charSet" form:"charSet" gorm:"column:charSet;comment:编码类型;size:50;"`

	UpdateCol   string `json:"updateCol" form:"updateCol" gorm:"column:updateCol;comment:更新列名;size:1000;"`
	NoCleanList string `json:"noCleanList" form:"noCleanList" gorm:"column:noCleanList;comment:不清除列表;size:1000;"`
	DiffProc    string `json:"diffProc" form:"diffProc" gorm:"column:diffProc;comment:数据不同处理方式;size:255;"`
	NewProc     string `json:"newProc" form:"newProc" gorm:"column:newProc;comment:新增处理方式;size:255;"`
	Dsn         string          `gorm:"-"`
	Conn        *ftp.ServerConn `gorm:"-"`
	Pwd         string          `gorm:"-"`
	StoreFile   string          `gorm:"-"`
	FtpFileList []*ftp.Entry    `gorm:"-"`
	Report      string          `json:"report",gorm:"-"`
}

func (m *OdsFtp) TableName() string {
	return "ods_ftp"
}

func (m *OdsFtp) Save() error {
	err := global.SysDB.Save(&m).Error
	return err
}

func (m *OdsFtp) SetDsn() error {
	if len(m.FtpIp) > 0 && len(m.FtpPort) > 0 {
		m.Dsn = m.FtpIp + ":" + m.FtpPort
		return nil
	}
	return fmt.Errorf("ftp ip或port为空")
}

func (m *OdsFtp) InitFtp() error {
	if len(m.FtpIp) > 0 && len(m.FtpPort) > 0 {
		m.Dsn = m.FtpIp + ":" + m.FtpPort
		msg := fmt.Sprintf("ftp dsn: %s", m.Dsn)
		m.Report += msg
	} else {
		msg := fmt.Sprintf("ftp ip(%s)或端口(%s)错误: %s", m.FtpIp, m.FtpPort, m.Dsn)
		m.Report += msg
		return fmt.Errorf(msg)
	}

	if m.FtpTimeOut == "" {
		m.FtpTimeOut = "5"
	}

	timeout, err := strconv.Atoi(m.FtpTimeOut)
	if err != nil {
		msg := fmt.Sprintf("...ftp配置超时(%s)配置不正确:%s\n", m.FtpTimeOut, err)
		m.Report += msg
		return fmt.Errorf(msg)
	}

	m.Conn, err = ftp.Dial(m.Dsn, ftp.DialWithTimeout(time.Duration(timeout)*time.Second))
	if err != nil {
		msg := fmt.Sprintf("...ftp连接服务器失败:%s\n", err)
		m.Report += msg
		return fmt.Errorf(msg)
	}
	m.Report += "...连接ftp成功\n"

	if err := m.Conn.Login(m.FtpUser, m.FtpPwd); err != nil {
		msg := fmt.Sprintf("...登录ftp失败,请检查用户名或密码或确认权限:%s\n", err)
		m.Report += msg
		return fmt.Errorf(msg)
	}
	m.Report += "...登录ftp成功\n"

	m.Pwd, err = m.Conn.CurrentDir()
	if err != nil {
		msg := fmt.Sprintf("...获取ftp默认路径失败:%s\n", err)
		m.Report += msg
		return fmt.Errorf(msg)
	}
	m.Report += "...ftp登录默认路径: " + m.Pwd + "\n"

	if m.FtpPath != "" {
		m.Report += "...ftp切换路径: " + m.FtpPath + "\n"
		err := m.Conn.ChangeDir(m.FtpPath)
		if err != nil {
			msg := fmt.Sprintf("...切换ftp指定路径(%s)失败:%s\n", m.FtpPath, err)
			m.Report += msg
			return fmt.Errorf(msg)
		}
		m.Pwd, err = m.Conn.CurrentDir()
		if err != nil {
			msg := fmt.Sprintf("...获取ftp当前路径失败: %s\n", err)
			m.Report += msg
			return fmt.Errorf(msg)
		}
		m.Report += "...ftp当前路径: " + m.Pwd + "\n"
	}

	if m.FtpFolder != "" {
		m.Report += "...ftp限定文件夹: " + m.FtpFolder + "\n"
		if m.FtpFolder[0] == '/' {
			m.Pwd += m.FtpFolder
		} else {
			m.Pwd += "/" + m.FtpFolder
		}
		m.Report += "...ftp限定文件夹: " + m.Pwd + "\n"
	}

	m.FtpFileList, err = m.Conn.List(m.Pwd)
	if err != nil {
		msg := fmt.Sprintf("...获取ftp当前目录(%s)文件列表失败:%s\n", m.Pwd, err)
		m.Report += msg
		return fmt.Errorf(msg)
	}
	if len(m.FtpFileList) <= 0 {
		msg := fmt.Sprintf("...ftp目录(%s)下文件数为0\n", m.Pwd)
		m.Report += msg
	} else {
		msg := fmt.Sprintf("...ftp目录(%s)下文件:\n", m.Pwd)
		m.Report += msg
		for i, v := range m.FtpFileList {
			file := strconv.Itoa(i) +
				", " + v.Name +
				", " + strconv.Itoa(int(v.Size)) +
				", " + strconv.Itoa(int(v.Type)) +
				", " + v.Time.String() +
				" " + v.Target + "\n"
			m.Report += file
		}
	}
	return nil
}

func (m *OdsFtp) Download() error {
	m.Report += fmt.Sprintf("\n开始ftp下载\n")
	if m.StorePath != "" {
		m.Report += fmt.Sprintf("设定存储路径: %s \n", m.StorePath)
		if m.StorePath[0] == '.' {
			cdir, err := os.Getwd()
			if err != nil {
				msg := fmt.Sprintf("获取当前目录失败: %s \n", err)
				m.Report += msg
				return fmt.Errorf(msg)
			}
			m.StorePath = cdir + m.StorePath[1:]
		}
		m.Report += fmt.Sprintf("绝对存储路径: %s \n", m.StorePath)
	} else {
		cdir, err := os.Getwd()
		if err != nil {
			msg := fmt.Sprintf("获取当前目录失败: %s \n", err)
			m.Report += msg
			return fmt.Errorf(msg)
		}
		m.StorePath = cdir + "/download"
		m.StorePath = strings.Replace(m.StorePath, "//", "/", 1)
		m.Report += fmt.Sprintf("默认存储路径: %s \n", m.StorePath)
	}

	exist, err := utils.PathExists(m.StorePath)
	if err != nil {
		msg := fmt.Sprintf("检查存储路径(%s)失败: %s \n", m.StorePath, err)
		m.Report += msg
		return fmt.Errorf(msg)
	}
	if !exist {
		if err := os.MkdirAll(m.StorePath, os.ModePerm); err != nil {
			msg := fmt.Sprintf("创建存储路径(%s)失败: %s \n", m.StorePath, err)
			m.Report += msg
			return fmt.Errorf(msg)
		}
		m.Report += fmt.Sprintf("创建存储路径: %s \n", m.StorePath)
	}

	if m.FtpFileName != "" {
		m.Report += fmt.Sprintf("限定下载文件名: %s \n", m.FtpFileName)
	}
	if m.FtpFileType != "" {
		m.Report += fmt.Sprintf("限定下载文件类型: %s \n", m.FtpFileType)
	}
	if m.FtpFileName != "" && m.FtpFileType != "" {
		m.Report += fmt.Sprintf("限定文件: %s \n", m.FtpFileName+"."+m.FtpFileType)
	}

	walker := m.Conn.Walk(m.Pwd)
	for walker.Next() {
		m.Report += fmt.Sprintf("ftp文件路径：%s\n", walker.Path())
		file := strings.Replace(walker.Path(), m.Pwd+"/", "", 1)
		m.Report += fmt.Sprintf("ftp文件：%s", file)
		fileList := strings.Split(file, ".")
		fileName := ""
		fileType := ""
		limitFile := ""
		if len(fileList) > 0 {
			fileName = fileList[0]
			m.Report += fmt.Sprintf(",文件名：%s", fileName)
			if m.FtpFileName != "" {
				limitFile = m.FtpFileName
			} else {
				limitFile = fileName
			}
		}
		if len(fileList) > 1 {
			fileType = fileList[1]
			m.Report += fmt.Sprintf(",文件类型：%s\n", fileType)
			if m.FtpFileType != "" {
				limitFile += "." + m.FtpFileType
			} else {
				limitFile += "." + fileType
			}
		}
		if file != limitFile {
			m.Report += fmt.Sprintf("忽略下载：%s\n", file)
			continue
		}

		m.StoreFile = strings.Replace(walker.Path(), m.Pwd, m.StorePath, 1)
		m.Report += fmt.Sprintf("保存为文件：%s\n", m.StoreFile)

		if walker.Stat().Type == ftp.EntryTypeFolder {
			m.Report += fmt.Sprintf("%s为文件夹,创建文件夹\n", walker.Path())
			os.MkdirAll(m.StoreFile, 0777)
		} else {
			func() {
				l, err := os.OpenFile(m.StoreFile, os.O_RDWR|os.O_CREATE, 0777)
				if err != nil {
					m.Report += fmt.Sprintf("创建本地文件(%s)失败:%s\n", m.StoreFile, err)
					return
				}
				m.Report += fmt.Sprintf("创建本地文件(%s)成功\n", m.StoreFile)
				defer l.Close()

				r, err := m.Conn.Retr(walker.Path())
				if err != nil {
					m.Report += fmt.Sprintf("下载文件(%s)失败：%s\n", walker.Path(), err)
					return
				}
				m.Report += fmt.Sprintf("下载文件(%s)成功\n", walker.Path())
				defer r.Close()

				io.Copy(l, r)
				m.Report += fmt.Sprintf("下载文件(%s)完成\n", walker.Path())

				if m.FtpDelAfterDownload {
					m.Conn.Delete(walker.Path())
					m.Report += fmt.Sprintf("删除ftp文件(%s)\n", walker.Path())
				}
			}()
		}
	}
	return nil
}

func (m *OdsFtp) Complate() {
	err := m.Conn.Quit()
	if err != nil {
		m.Report += fmt.Sprintf("ftp连接退出失败：%s", err)
	}
	m.Report += fmt.Sprintf("ftp连接退出成功")
}







